#ifndef GLVIEW_H
#define GLVIEW_H

#include <process.h>

extern CRITICAL_SECTION CriticalSection;  
class GLView : public View 
{
public:
	GLView(Model * model, HINSTANCE hins, LPCTSTR name , HWND parent = NULL): model (model), View (hins, name, parent)
	{
	}
	virtual ~ GLView () 
	{
	}

public:
	void EnableOpenGL()
	{
		if (!createContext(hwnd, 32, 24, 8))
		{
			::MessageBox(0, L"Cannot create Context.", L"Error", MB_ICONEXCLAMATION | MB_OK);
		}
		
		

		threadHandle = (HANDLE)_beginthreadex(0, 0, (unsigned (__stdcall *)(void *))threadFunction, this, 0, &threadId);
		if(threadHandle)
		{
			loopFlag = true;
			//Win::log(L"Created a rendering thread for OpenGL.");
		}
		else
		{
			::MessageBox(0, L"Cannot thread.", L"Error", MB_ICONEXCLAMATION | MB_OK);
		}
	}

	void Draw ()
	{
		//::MessageBox(0, L"Draw.", L"Error", MB_ICONEXCLAMATION | MB_OK);
		//model->draw(); 
		//SwapBuffers( hDC );
	}
	void DisableOpenGL()
	{
		loopFlag = false;
		::WaitForSingleObject(threadHandle, INFINITE);   
		closeContext();
	}
	
protected:


	static void threadFunction(void* param)
	{
		((GLView*)param)->renderFunction();
	}


	virtual void renderFunction()
	{
		::wglMakeCurrent(hDC, hRC);
		//Apply the Shader here
		EnterCriticalSection(&CriticalSection); 
		glewInit();
		//shader.load("D:\\workspace\\Brush\\glsl\\mcolor.vert", "D:\\workspace\\Brush\\glsl\\mcolor.frag");
		shader.load("D:\\workspace\\Brush\\glsl\\pointlight.vert", "D:\\workspace\\Brush\\glsl\\pointlight.frag");
		//shader.load("D:\\workspace\\Brush\\glsl\\mcolor.vert", "D:\\workspace\\Brush\\glsl\\nearest.frag");
		//shader.load("D:\\workspace\\windowmanager\\glsl\\pointlight.vert", "D:\\workspace\\windowmanager\\glsl\\pointlight.frag");

		shader.bind();
		LeaveCriticalSection(&CriticalSection);
		
		model->setUpdated(model->cullingView, true);
		
		model->init();
		while(loopFlag)
		{
			::Sleep(1);   
			EnterCriticalSection(&CriticalSection); 
			if (model->getUpdated(model->cullingView) == true) 
			{
				model->setShader(& shader);
				model->draw_perspective(); 
				//model->draw_frustum();
				::SwapBuffers(hDC);
				model->setUpdated(model->cullingView, false);
			}
			LeaveCriticalSection(&CriticalSection);
		}
		::wglMakeCurrent(0, 0);
		::CloseHandle(threadHandle);
	}

	bool createContext(HWND handle, int colorBits, int depthBits, int stencilBits)
	{
		hDC = ::GetDC(handle);
		if(!setPixelFormat(hDC, colorBits, depthBits, stencilBits))
		{
			::MessageBox(0, L"Cannot set a suitable pixel format.", L"Error", MB_ICONEXCLAMATION | MB_OK);
			::ReleaseDC(handle, hDC);
			return false;
		}

		// create a new OpenGL rendering context
		hRC = ::wglCreateContext(hDC);
		//::wglMakeCurrent(hdc, hglrc);

		::ReleaseDC(handle, hDC);
		return true;
	}

	bool setPixelFormat(HDC hdc, int colorBits, int depthBits, int stencilBits)
	{
		PIXELFORMATDESCRIPTOR pfd;

		// find out the best matched pixel format
		int pixelFormat = findPixelFormat(hdc, colorBits, depthBits, stencilBits);
		if(pixelFormat == 0)
			return false;

		// set members of PIXELFORMATDESCRIPTOR with given mode ID
		::DescribePixelFormat(hdc, pixelFormat, sizeof(pfd), &pfd);

		// set the fixel format
		if(!::SetPixelFormat(hdc, pixelFormat, &pfd))
			return false;

		return true;
	}

	int findPixelFormat(HDC hdc, int colorBits, int depthBits, int stencilBits)
	{
		int currMode;                               // pixel format mode ID
		int bestMode = 0;                           // return value, best pixel format
		int currScore = 0;                          // points of current mode
		int bestScore = 0;                          // points of best candidate
		PIXELFORMATDESCRIPTOR pfd;

		// search the available formats for the best mode
		bestMode = 0;
		bestScore = 0;
		for(currMode = 1; ::DescribePixelFormat(hdc, currMode, sizeof(pfd), &pfd) > 0; ++currMode)
		{
			// ignore if cannot support opengl
			if(!(pfd.dwFlags & PFD_SUPPORT_OPENGL))
				continue;

			// ignore if cannot render into a window
			if(!(pfd.dwFlags & PFD_DRAW_TO_WINDOW))
				continue;

			// ignore if cannot support rgba mode
			if((pfd.iPixelType != PFD_TYPE_RGBA) || (pfd.dwFlags & PFD_NEED_PALETTE))
				continue;

			// ignore if not double buffer
			if(!(pfd.dwFlags & PFD_DOUBLEBUFFER))
				continue;

			// try to find best candidate
			currScore = 0;

			// colour bits
			if(pfd.cColorBits >= colorBits) ++currScore;
			if(pfd.cColorBits == colorBits) ++currScore;

			// depth bits
			if(pfd.cDepthBits >= depthBits) ++currScore;
			if(pfd.cDepthBits == depthBits) ++currScore;

			// stencil bits
			if(pfd.cStencilBits >= stencilBits) ++currScore;
			if(pfd.cStencilBits == stencilBits) ++currScore;

			// alpha bits
			if(pfd.cAlphaBits > 0) ++currScore;

			// check if it is best mode so far
			if(currScore > bestScore)
			{
				bestScore = currScore;
				bestMode = currMode;
			}
		}

		return bestMode;
	}

	void closeContext()
	{
		if(!hDC || !hRC)
			return;

		// delete DC and RC
		::wglMakeCurrent(0, 0);
		::wglDeleteContext(hRC);
		::ReleaseDC(hwnd, hDC);

		hRC = 0;
		hDC = 0;
	}

protected:
	HDC hDC;
	HGLRC hRC;

	HANDLE threadHandle;
	unsigned int threadId;
	volatile bool loopFlag;

	Model * model;
	MeshColorShader shader; 
	//MeshColorShader nearestShader; 
};


#endif